/********************************************************************

Write Date: 2011/1/27
Author: Deng Nianchen
>>>> Implement general functions of a smart pointer.

Modify Date: 2011/3/22
Author: Deng Nianchen
>>>> Add cvt() function to support type convertion.
>>>> Add to OOP namespace.
>>>> Use OOPException as exception framework.

Modify Date: 2011/03/24
Author: Deng Nianchen
>>>> Remove support for array pointer. (I think it is unnecessary)

Modify Date: 2011/06/02
Author: Deng Nianchen
>>>> Split Ptr to two classes: HeapPtr and OOPPtr. See HeapPtr.hpp
     and OOPPtr.hpp for detail. Now this file contains only the common
     implementation of HeapPtr and OOPPtr and named PtrBase.
>>>> Add an exception TypeConvertFailException.
>>>> Changes in exceptions follow the change of OOPException.

Modify Date: 2011/07/16
Author: Deng Nianchen
>>>> Move class PtrException and its children to PtrExceptions.hpp.

Modify Date: 2015/04/20
Author: Apple
>>>> Merge Ptr, HeapPtr and OOPPtr.
>>>> Rename class OOPPtrHelper to ObjectRefCountHandler and move to
	 a new file.
>>>> Update the style of documents and codes.

Modify Date: 2015/04/22
Author: Apple
>>>> Move the reference management to class ObjectRef.

Modify Date: 2015/04/26
Author: Apple
>>>> Move cvt() to class ObjectRef.
>>>> Add const to the paremeter of copy constructor and copy assignment.

Modify Date: 2015/05/05
Author: Apple
>>>> Fix the bug: Cannot release the data in the Wrapper.

Modify Date: 2015/05/07
Author: Apple
>>>> Add const version of operator->(), operator*() and getExternRef()
>>>> Add const qualifier to _getPtrToRealObject().
>>>> Complete the document.

Modify Date: 2015/8/12
Author: Apple
>>>> Move operator== to ObjectRef.

Modify Date: 2015/08/17
Author: Apple
>>>> Modify the include according to the renameing of PtrExceptions.h

*********************************************************************/
#pragma once
#include "ooputils_global.h"
#include "Object.h"
#include "ObjectRef.h"
#include "CommonExceptions.h"
#include "PtrExceptions.h"



_NS_OOP_BEGIN



/**
 * Class Ptr encapsulate C++ pointer so developers didn't need to worry 
 * whether the object created by new operator has been deleted or not. 
 * This implementation is suitable for both Object pointers and other pointer 
 * type. 
 * Note:
 * 1. Because class Object has the ability to mark itself as heap-created or
 * stack-created, it is safe to use Ptr for Object objects created both on stack
 * and on heap. But use it for other objects created on stack will cause crash.
 * 2. It cannot be used to raw array (for example, created by operator new[]).
 * 3. It cannot be used to a memery created by malloc().
 * 3. use OOPNEW macro to create an object and get a Ptr to it.
 * 4. If you need to get the raw pointer (for example, to be used in an external 
 *    system), you can call getExternRef () and don't forget to call 
 *    releaseExternRef () later.
 * 5. If you need to get a Ptr to a existed pointer, refer to class NewPtr and 
 *    be sure there are no references to the object in other places at that time.
 *    When no Ptrs point to the object, the object will be destroied 
 *    automatically.
 * 6. To set a Ptr to NULL, use setNull().
 *
 * Thread-Safe: yes
 *
 * @remarks 2015/04/20, Apple
 */
template< class T >
class Ptr : public ObjectRef
{

	friend class ResourceLoader;
	friend class ResourceSaver;
	friend class ObjectRef;

public:

	/**
	 * Create a Ptr pointing to NULL.
	 */
	Ptr ();

	/**
	 * Copy constructor.
	 *
	 * @param r_right - the source Ptr
	 */
	Ptr (const Ptr< T >& r_right);

	/**
	 * Create a Ptr and move another Ptr's target to it. The source Ptr will be 
	 * set to NULL.
	 *
	 * @param rr_right - the source Ptr
	 */
	Ptr (Ptr< T >&& rr_right);

	/**
	 * Create a Ptr pointing to another Ptr's target. Implicit cast is support.
	 *
	 * @param r_right - the source Ptr
	 */
	template< class T2 >
	Ptr (const Ptr< T2 >& r_right);

	/**
	 * Destroy a Ptr.
	 */
	~Ptr ();

	/**
	 * Copy assignment.
	 *
	 * @param r_right - the source Ptr
	 * @return *this
	 */
	Ptr< T >& operator= (const Ptr< T >& r_right);

	/**
	 * Move another Ptr's target to this. The source Ptr will be set to NULL.
	 *
	 * @param rr_right - the source Ptr
	 * @return *this
	 */
	Ptr< T >& operator= (Ptr< T >&& rr_right);

	/**
	 * Point to another Ptr's target. Implicit cast is support.
	 *
	 * @param r_right - the source Ptr
	 * @return *this
	 */
	template< class T2 >
	Ptr< T >& operator= (const Ptr< T2 >& r_right);

	/**
	 * Provide the same function as normal pointer.
	 *
	 * @return the normal pointer pointing to the target
	 * @throw NullPtrException - If the Ptr is pointing to NULL
	 */
	T* operator-> () throw (NullPtrException);
	const T* operator-> () const throw (NullPtrException);

	/**
	 * Provide dereference operation.
	 *
	 * @return the reference to the target
	 * @throw NullPtrException - If the Ptr is pointing to NULL
	 */
	T& operator* () throw (NullPtrException);
	const T& operator* () const throw (NullPtrException);

	/**
	 * Get the normal pointer pointing to the target to be used in external 
	 * system/library. The reference count of the target will increase by one.
	 * NOTE: getExternRef() and releaseExternRef() MUST appear in pairs.
	 *
	 * @return the normal pointer pointing to the target or NULL if the Ptr is 
	 *         pointing to NULL
	 */
	T* getExternRef ();
	const T* getExternRef () const;

	/**
	 * Release the extern reference. The reference count of the target will 
	 * decrease by one.
	 * NOTE: getExternRef() and releaseExternRef() MUST appear in pairs.
	 */
	void releaseExternRef ();

protected:

	/**
	 * Create a Ptr from an Object pointer. Invoked by NewPtr.
	 *
	 * @param p_obj - the Object pointer
	 */
	Ptr (Object* p_obj);

	/**
	 * Override ObjectRef::__release(), release non-Object data if T is 
	 * non-Object.
	 */
	virtual void __release ();

private:

	/**
	 * Get the normal pointer pointing to the target. 
	 *
	 * @return the normal pointer pointing to the target
	 */
	T* _getPtrToRealObject () const;

	/**
	 * Release non-Object data if T is non-Object.
	 */
	void _releaseNonObjectData ();

};



_NS_OOP_END



#include "Ptr.hpp"